// Copyright (c) 2010 god6or@gmail.com under MIT license.

package ul.ownet

import collection.mutable.{ListBuffer,ArrayBuffer}
import java.awt.{Dimension, Color, Font}
import javax.swing.{DefaultListModel, ListSelectionModel, SwingConstants, SwingUtilities, border, JSpinner, SpinnerNumberModel, event}
import java.util.concurrent.atomic.{AtomicBoolean}

import swing.{Swing,Orientation,Action,Panel,BoxPanel,BorderPanel,Label,Button,ComboBox,TabbedPane,Component,ListView,TextField}
import swing.event._
import actors.{Actor}

import ul.iobj.{IAttr,IObj,IObjT}
import ul.gettext.{GetText}
import ul.comm.{Serial, OneWire, OneWireSerial, OneWireThermometer}


class CfgPanel( parentTabs: TabbedPane ) extends TabbedPane.Page("", new Label("")) {

    val ppage = this
    val tr: (String)=>String = Config.tran.tr _
    
    title = tr("Configuration")
    tip = tr("Configuration page")
    
    val listFont = new Font( Font.MONOSPACED, Font.PLAIN, 12)
    
    // update sensors listView content/selection from sensors array
    def updateSensorsList: Unit = {
        SwingUtilities.invokeLater(
            new Thread {
                override def run {
                    //println("sensors: " + sensors)
                    downPanel.sensorsList.sensors = (for (s <- Sensors.sensors) yield s.toListString)
                    downPanel.sensorsList.sensorsSel = {
                        val ss = new ArrayBuffer[Int]
                        for (i <- 0 to Sensors.sensors.length-1 if (Sensors.sensors(i).selected)) ss += i
                        ss
                    }
                }
            }
        )
    }
    // update sensors selection flags from listView selection
    def updateSensorsSelection: Unit = {
        if (Sensors.sensors.length > 0 && Sensors.sensors.length == downPanel.sensorsList.peer.getModel.getSize) {
            for (s <- Sensors.sensors) s.selected = false // clear all selection flags
            for (i <- downPanel.sensorsList.sensorsSel) Sensors.sensors(i).selected = true // set selected
        }
    }

    lazy val guiWidgets = List(parentTabs,upPanel.langSel,downPanel.portsList,downPanel.portsUpdateButton,
        downPanel.sensorsList,downPanel.sensorsUpdateButton,downPanel.sensorsReadButton,
        downPanel.sensorNameSetButton,downPanel.sensorNameText,downPanel.sensorUpButton,
        downPanel.sensorDownButton, downPanel.sensorCorrButton
    )
    // enable/disable GUI on important operations
    def enableGUI( en: Boolean ): Unit = {
        SwingUtilities.invokeLater(
            new Thread {
                override def run {
                    for (w <- guiWidgets) w.enabled = en
                }
            }
        )
    }

    object sensorsDetectCompleteDetector extends Actor {
        start
        def act { loop { react {
                    case Sensors.SensorsDetectComplete =>
                        if (parentTabs.selection.index == 0) {
                            enableGUI(true)
                            updateSensorsList
                        }
                    case _ =>
        }}}
    }
    Sensors.sensorsDetectCompleteDetectors += sensorsDetectCompleteDetector
    
    object sensorsReadCompleteListener extends Actor {
        start
        def act { loop { react {
                    case Sensors.SensorsReadComplete( updateDB ) =>
                        if (parentTabs.selection.index == 0) {
                            enableGUI(true)
                            updateSensorsList
                        }
                    case _ =>
        }}}
    }
    Sensors.SensorsReader ! Sensors.AddReadCompleteListener(sensorsReadCompleteListener)

    val upPanel = new BoxPanel(Orientation.Horizontal) { // upper panel

        val langSel = new ComboBox( Config.tran.displayLangs )
        langSel.selection.index = if (Config.tran.langs.indexOf( Config("lang").s ) == -1) 0 else Config.tran.langs.indexOf( Config("lang").s )

        contents ++= List(
            Swing.HStrut(5),
            new Label( tr("language: ") ) {foreground = Color.BLUE; tooltip = tr("select interface language")},
            langSel,
            Swing.HGlue,
            Swing.HGlue
        )

    }
    val downPanel = new BoxPanel(Orientation.Vertical) { // lower panel
        val downpanel = this
        
        // ports list
        val portsList = new ListView {
            val model = new DefaultListModel; peer.setModel( model )
            peer.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION )
            xLayoutAlignment = 0.0F
            peer.setVisibleRowCount( 3 )
            font = listFont
            
            ports = Serial.ports
            portsSel = Config.attr("ports").s.split(";")
            
            def ports: Seq[String] = for (pi <- 0 to model.getSize()-1) yield model.getElementAt(pi).toString
            def ports_= (ps: Seq[String]) = {
                model.clear
                for ( p <- ps ) model.addElement(p)
            }
            def portsSel: Seq[String] = {
                for (p <- peer.getSelectedValues()) yield p.toString
            }
            def portsSel_= (ps: Seq[String]) = {
                val ports = this.ports
                for (p <- ps if ports.indexOf(p) >= 0)
                    peer.addSelectionInterval(ports.indexOf(p), ports.indexOf(p))
            }
        }
        
        // ports list update button
        val portsUpdateButton = new Button { action = new Action( tr("update") ) {
            toolTip = tr("update ports list")
            xLayoutAlignment = 0.0F
            override def apply() = {
                val ps = portsList.portsSel
                portsList.ports = Serial.ports
                portsList.portsSel = ps
            }
        }}

        // sensors list
        val sensorsList = new ListView {
            val model = new DefaultListModel; peer.setModel( model )
            peer.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION )
            peer.setVisibleRowCount( 10 )
            xLayoutAlignment = 0.0F
            font = listFont
            
            def sensors: Seq[String] = for (di <- 0 to model.getSize()-1) yield model.getElementAt(di).toString
            def sensors_= (ds: Seq[String]) = {
                model.clear
                for ( d <- ds ) model.addElement(d)
            }
            def sensorsSel: Seq[Int] = selection.indices.toList
            def sensorsSel_= (ds: Seq[Int]) = {
                peer.clearSelection()
                for (di <- ds) peer.addSelectionInterval(di, di)
                //selection.indices.clear
                //for (di <- ds) selection.indices += di
            }

        }

        // sensors list update button
        val sensorsUpdateButton: Button = new Button { action = new Action( tr("update") ) {
            toolTip = tr("update sensors list")
            xLayoutAlignment = 0.0F
            override def apply() = {
                val b = this
                if (!Sensors.sensorsAreDetected.get() && (portsList.portsSel.length > 0)) {
                    enableGUI(false)
                    Sensors.SensorsDetector ! Sensors.SensorsDetect( portsList.portsSel )
                }
            }
        }}
        val sensorsReadButton = new Button { action = new Action( tr("read") ) {
            toolTip = tr("read temperature")
            xLayoutAlignment = 0.0F
            override def apply() = {
                if (!Sensors.sensorsAreRead.get() && (Sensors.sensors.length > 0)) {
                    updateSensorsSelection
                    enableGUI(false)
                    Sensors.SensorsReader ! Sensors.SensorsRead( false )
                }
            }
        }}

        val sensorNameText = new TextField(20) { maximumSize = new Dimension(100, 40) }
        val sensorNameSetButton = new Button { action = new Action( tr("set") ) {
            toolTip = tr("set sensor name")
            xLayoutAlignment = 0.0F
            override def apply() = {
                if ((sensorsList.selection.indices.size == 1)&&(sensorNameText.text.length > 0)&&(sensorNameText.text.length <= 20)) {
                    Sensors.sensors(sensorsList.selection.indices.toList(0)).name = sensorNameText.text
                    updateSensorsSelection
                    updateSensorsList
                }
            }
        }}

        val sensorUpButton = new Button { action = new Action( tr("up") ) {
            toolTip = tr("move sensor up")
            xLayoutAlignment = 0.0F
            override def apply() = {
                if ((Sensors.sensors.length > 0)&&(sensorsList.selection.indices.size == 1)&&(sensorsList.selection.indices.toList(0) > 0)) {
                    updateSensorsSelection
                    val i = sensorsList.selection.indices.toList(0)
                    val s = Sensors.sensors(i-1)
                    Sensors.sensors(i-1) = Sensors.sensors(i)
                    Sensors.sensors(i) = s
                    updateSensorsList
                }
            }
        }}
        val sensorDownButton = new Button { action = new Action( tr("down") ) {
            toolTip = tr("move sensor down")
            xLayoutAlignment = 0.0F
            override def apply() = {
                if ((Sensors.sensors.length > 0)&&(sensorsList.selection.indices.size == 1)&&(sensorsList.selection.indices.toList(0) < Sensors.sensors.length-1)) {
                    updateSensorsSelection
                    val i = sensorsList.selection.indices.toList(0)
                    val s = Sensors.sensors(i+1)
                    Sensors.sensors(i+1) = Sensors.sensors(i)
                    Sensors.sensors(i) = s
                    updateSensorsList
                }
            }
        }}
        
        val sensorCorrSpinner = new JSpinner(new SpinnerNumberModel(0.0, -10.0, 10.0, 0.1)) {
            setMaximumSize( new Dimension( 50, 30 ) )
        }
        val sensorCorrButton = new Button { action = new Action( tr("set") ) {
            toolTip = tr("set sensor correction")
            xLayoutAlignment = 0.0F
            override def apply() = {
                if (sensorsList.selection.indices.size == 1) {
                    Sensors.sensors(sensorsList.selection.indices.toList(0)).correction =
                        sensorCorrSpinner.getModel.asInstanceOf[SpinnerNumberModel].getNumber.doubleValue;
                    updateSensorsSelection
                    updateSensorsList
                }
            }
        }}

        val pollPeriodSpinner = new JSpinner(new SpinnerNumberModel(Config("pollPeriod").i, 4, 1000, 1)) {
            setMaximumSize( new Dimension( 50, 30 ) )
            addChangeListener( pollPeriodSpinnerListener )
        }
        // update poll period value in Config
        object pollPeriodSpinnerListener extends event.ChangeListener {
            def stateChanged(e: event.ChangeEvent) {
                Config("pollPeriod").i = pollPeriodSpinner.getValue().asInstanceOf[Number].intValue()
            }
        }
        
        contents ++= List(
            Swing.VStrut(5)
            ,new BoxPanel(Orientation.Vertical) {
                peer.setBorder( new javax.swing.border.EtchedBorder )
                contents ++= List(
                    new BoxPanel(Orientation.Horizontal) {
                        xLayoutAlignment = 0.0F
                        yLayoutAlignment = 0.0F
                        contents += new Label( tr("ports: ") ) {
                            foreground = Color.RED
                            tooltip = tr("ports list")
                        }
                        contents += portsUpdateButton
                    },
                    Swing.VStrut(5),
                    portsList,
                    Swing.VStrut(5)
                )
            }
            ,Swing.VStrut(5)
            ,new BoxPanel(Orientation.Vertical) {
                peer.setBorder( new javax.swing.border.EtchedBorder )
                contents ++= List(
                    new BoxPanel(Orientation.Horizontal) {
                        xLayoutAlignment = 0.0F
                        yLayoutAlignment = 0.0F
                        
                        contents ++= List(
                            new Label( tr("sensors: ") ) {
                                tooltip = tr("sensors list")
                                //peer.setHorizontalAlignment( SwingConstants.LEFT )
                                //xLayoutAlignment = 0.0F
                                foreground = Color.RED
                            },
                            sensorsUpdateButton,
                            sensorsReadButton,
                            sensorUpButton,
                            sensorDownButton
                        )
                    }
                    ,Swing.VStrut(5)
                    ,new BoxPanel(Orientation.Vertical) {
                        peer.setBorder( new javax.swing.border.EtchedBorder )
                        contents ++= List(
                            new Label( format(Sensor.fmtStr, tr("number"), tr("type"), tr("name"), "°C", tr("correction")) ) {
                                xLayoutAlignment = 0.0F
                                foreground = Color.BLUE
                                font = listFont
                            },
                            sensorsList
                        )
                    }
                    ,Swing.VStrut(5)
                    ,new BoxPanel(Orientation.Horizontal) {
                        xLayoutAlignment = 0.0F
                        yLayoutAlignment = 0.0F
                        contents ++= List(
                            new Label( tr("name: ") ) {foreground = Color.BLUE; tooltip = tr("sensor name")},
                            sensorNameText,
                            sensorNameSetButton,
                            Swing.HGlue
                        )
                    }
                    ,Swing.VStrut(5)
                    ,new BoxPanel(Orientation.Horizontal) {
                        xLayoutAlignment = 0.0F
                        yLayoutAlignment = 0.0F
                        contents += new Label( tr("correction: ") ) {foreground = Color.BLUE; tooltip = tr("temperature correction")}
                        peer.add( sensorCorrSpinner )
                        contents += sensorCorrButton
                        contents += Swing.HGlue
                    }
                    //,Swing.VStrut(5)
                    //Swing.VGlue
                )
            }
            ,Swing.VStrut(5)
            ,new BoxPanel(Orientation.Vertical) {
                peer.setBorder( new javax.swing.border.EtchedBorder )
                contents ++= List(
                    new BoxPanel(Orientation.Horizontal) {
                        xLayoutAlignment = 0.0F
                        yLayoutAlignment = 0.0F
                        contents += new Label( tr("poll period, sec: ") ) {foreground = Color.BLUE; tooltip = tr("poll period, seconds")}
                        peer.add( pollPeriodSpinner )
                        contents += Swing.HGlue
                    }
                )
            }
            ,Swing.VGlue
        )
        
        listenTo( sensorsList.selection )
        reactions += {

            case SelectionChanged( `sensorsList` ) => {
                //updateSensorsSelection
                if (sensorsList.selection.indices.size == 1) {
                    val i = sensorsList.selection.indices.toList(0)
                    sensorNameText.text = Sensors.sensors(i).name
                    sensorCorrSpinner.setValue( Sensors.sensors(i).correction )
                }
            }

        }
        
    }

    val mainPanel = new BorderPanel {
        add( upPanel, BorderPanel.Position.North )
        add( downPanel, BorderPanel.Position.Center )
    }

    content = mainPanel
    fromCfg;
    updateSensorsList;
    

    def fromCfg = {
        // update controls from configuration
        Sensors.sensors.clear
        for (sl <- Config.attr("sensors").s.split("::")) {
            try {
                val ssl = sl.split(";")
                Sensors.sensors += new Sensor(ssl(2).toInt, ssl(3).toLong, ssl(0), ssl(1), ssl(4).toBoolean, ssl(5).toDouble)
            } catch { case _ => }
        }
    }
    def toCfg = {
        Config("lang").s = Config.tran.langs( upPanel.langSel.selection.index )
        
        var s = ""
        for (p <- downPanel.portsList.portsSel) s += p + ";"
        Config("ports").s = s
        
        s = ""
        updateSensorsSelection
        for (ss <- Sensors.sensors) s += ss.toString + "::"
        Config("sensors").s = s
        
        Config("pollPeriod").i = downPanel.pollPeriodSpinner.getValue().asInstanceOf[Number].intValue()
    }
    
}

